/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.l2j.gameserver.model;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javolution.util.FastList;
import net.sf.l2j.gameserver.GeoData;
import net.sf.l2j.gameserver.datatables.HeroSkillTable;
import net.sf.l2j.gameserver.datatables.SkillTable;
import net.sf.l2j.gameserver.datatables.SkillTreeTable;
import net.sf.l2j.gameserver.instancemanager.CoupleManager;
import net.sf.l2j.gameserver.model.actor.instance.L2ArtefactInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2ChestInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2PetInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2PlayableInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2SummonInstance;
import net.sf.l2j.gameserver.model.base.ClassId;
import net.sf.l2j.gameserver.model.entity.Couple;
import net.sf.l2j.gameserver.network.SystemMessageId;
import net.sf.l2j.gameserver.network.serverpackets.EtcStatusUpdate;
import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
import net.sf.l2j.gameserver.skills.Env;
import net.sf.l2j.gameserver.skills.Stats;
import net.sf.l2j.gameserver.skills.conditions.Condition;
import net.sf.l2j.gameserver.skills.effects.EffectCharge;
import net.sf.l2j.gameserver.skills.effects.EffectTemplate;
import net.sf.l2j.gameserver.skills.funcs.Func;
import net.sf.l2j.gameserver.skills.funcs.FuncTemplate;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillCharge;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillChargeDmg;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillCreateItem;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillDefault;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillDrain;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillExitBuffs;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillNeedCharge;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillSeed;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillSignet;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillSignetCasttime;
import net.sf.l2j.gameserver.skills.l2skills.L2SkillSummon;
import net.sf.l2j.gameserver.templates.StatsSet;
import net.sf.l2j.gameserver.util.Util;

/**
 * This class...
 * 
 * @version $Revision: 1.3.2.8.2.22 $ $Date: 2005/04/06 16:13:42 $
 */
public abstract class L2Skill implements IChanceSkillTrigger
{
	protected static final Logger _log = Logger.getLogger(L2Skill.class.getName());
	public static final int SKILL_CUBIC_MASTERY = 143;
	public static final int SKILL_LUCKY = 194;
	public static final int SKILL_CREATE_COMMON = 1320;
	public static final int SKILL_CREATE_DWARVEN = 172;
	public static final int SKILL_CRYSTALLIZE = 248;
	public static final int SKILL_DIVINE_INSPIRATION = 1405;
	public static final int SKILL_FAKE_INT = 9001;
	public static final int SKILL_FAKE_WIT = 9002;
	public static final int SKILL_FAKE_MEN = 9003;
	public static final int SKILL_FAKE_CON = 9004;
	public static final int SKILL_FAKE_DEX = 9005;
	public static final int SKILL_FAKE_STR = 9006;
	// clan skills
	public static final int SKILL_CLAN_LUCK = 390;

	public static enum SkillOpType 
	{
		OP_PASSIVE,
		OP_ACTIVE, 
		OP_TOGGLE
	}

	/** Target types of skills : SELF, PARTY, CLAN, PET... */
	public static enum SkillTargetType 
	{
		TARGET_NONE,
		TARGET_SELF,
		TARGET_ONE,
		TARGET_PARTY,
		TARGET_ALLY,
		TARGET_CLAN,
		TARGET_PET,
		TARGET_AREA,
		TARGET_AURA,
		TARGET_FRONT_AURA,
		TARGET_BEHIND_AURA,
		TARGET_CORPSE, 
		TARGET_UNDEAD,
		TARGET_AREA_UNDEAD, 
		TARGET_MULTIFACE,
		TARGET_CORPSE_ALLY, 
		TARGET_CORPSE_CLAN, 
		TARGET_CORPSE_PLAYER,
		TARGET_CORPSE_PET,
		TARGET_ITEM, 
		TARGET_AREA_CORPSE_MOB,
		TARGET_CORPSE_MOB, 
		TARGET_UNLOCKABLE,
		TARGET_HOLY,
		TARGET_PARTY_MEMBER,
		TARGET_ENEMY_SUMMON,
		TARGET_OWNER_PET,
		TARGET_ENEMY_ALLY, 
		TARGET_PIG, 
		TARGET_COUPLE, 
		TARGET_PARTY_OTHER,
		TARGET_GROUND
	}

	public static enum SkillType
	{
		PDAM,
		MDAM,
		CPDAM,
		MANADAM,
		DOT,
		MDOT,
		DRAIN_SOUL,
		DRAIN(L2SkillDrain.class), 
		DEATHLINK, 
		BLOW,
		FATALCOUNTER,
		BLEED,
		POISON,
		STUN,
		ROOT, 
		CONFUSION,
		FEAR,
		SLEEP,
		CONFUSE_MOB_ONLY,
		MUTE, 
		PARALYZE,
		WEAKNESS,
		HEAL,
		HOT,
		BALANCE_LIFE, 
		HEAL_PERCENT,
		HEAL_STATIC, 
		COMBATPOINTHEAL, 
		CPHOT, MANAHEAL, 
		MANA_BY_LEVEL,
		MANAHEAL_PERCENT, 
		MANARECHARGE, 
		MPHOT, 
		COMBATPOINTPERHEAL,
		GIVE_SP,
		MOG,
		AGGDAMAGE,
		AGGREDUCE, 
		AGGREMOVE,
		AGGREDUCE_CHAR,
		AGGDEBUFF,
		FISHING,
		PUMPING, 
		REELING,
		UNLOCK, 
		ENCHANT_ARMOR,
		ENCHANT_WEAPON,
		SOULSHOT,
		SPIRITSHOT,
		SIEGEFLAG, 
		TAKECASTLE, 
		WEAPON_SA,
		DELUXE_KEY_UNLOCK,
		SOW, 
		HARVEST,
		GET_PLAYER,
		COMMON_CRAFT,
		DWARVEN_CRAFT,
		CREATE_ITEM(L2SkillCreateItem.class),
		SUMMON_TREASURE_KEY,
		SUMMON(L2SkillSummon.class),
		FEED_PET, 
		DEATHLINK_PET,
		STRSIEGEASSAULT, 
		ERASE, 
		BETRAY,
		CANCEL,
		MAGE_BANE,
		WARRIOR_BANE,
		NEGATE,
		BUFF,
		DEBUFF,
		PASSIVE,
		CONT, 
		SIGNET(L2SkillSignet.class),
		SIGNET_CASTTIME(L2SkillSignetCasttime.class),
		RESURRECT, CHARGE(L2SkillCharge.class), 
		MHOT,
		CHARGEDAM(L2SkillChargeDmg.class),
		EXITBUFFS(L2SkillExitBuffs.class),
		NEEDCHARGE(L2SkillNeedCharge.class),
		DETECT_WEAKNESS,
		LUCK, 
		RECALL,
		SUMMON_FRIEND,
		REFLECT,
		SPOIL,
		SWEEP,
		FAKE_DEATH, 
		UNBLEED,
		UNPOISON, 
		UNDEAD_DEFENSE,
		SEED(L2SkillSeed.class),
		BEAST_FEED,
		FORCE_BUFF,
		ZAKENPLAYER,
		ZAKENSELF,
		NOTDONE;
		
		private final Class<? extends L2Skill> _class;
		public L2Skill makeSkill(StatsSet set) 
		{
			try 
			{
				Constructor<? extends L2Skill> c = _class
						.getConstructor(StatsSet.class);
				return c.newInstance(set);
			} 
			catch (Exception e) 
			{
				throw new RuntimeException(e);
			}
		}

		private SkillType()
		{
			_class = L2SkillDefault.class;
		}

		private SkillType(Class<? extends L2Skill> classType)
		{
			_class = classType;
		}
	}

	// elements
	public final static int ELEMENT_WIND = 1;
	public final static int ELEMENT_FIRE = 2;
	public final static int ELEMENT_WATER = 3;
	public final static int ELEMENT_EARTH = 4;
	public final static int ELEMENT_HOLY = 5;
	public final static int ELEMENT_DARK = 6;
	// save vs
	public final static int SAVEVS_INT = 1;
	public final static int SAVEVS_WIT = 2;
	public final static int SAVEVS_MEN = 3;
	public final static int SAVEVS_CON = 4;
	public final static int SAVEVS_DEX = 5;
	public final static int SAVEVS_STR = 6;
	// stat effected
	public final static int STAT_PATK = 301; // pAtk
	public final static int STAT_PDEF = 302; // pDef
	public final static int STAT_MATK = 303; // mAtk
	public final static int STAT_MDEF = 304; // mDef
	public final static int STAT_MAXHP = 305; // maxHp
	public final static int STAT_MAXMP = 306; // maxMp
	public final static int STAT_CURHP = 307;
	public final static int STAT_CURMP = 308;
	public final static int STAT_HPREGEN = 309; // regHp
	public final static int STAT_MPREGEN = 310; // regMp
	public final static int STAT_CASTINGSPEED = 311; // sCast
	public final static int STAT_ATKSPD = 312; // sAtk
	public final static int STAT_CRITDAM = 313; // critDmg
	public final static int STAT_CRITRATE = 314; // critRate
	public final static int STAT_FIRERES = 315; // fireRes
	public final static int STAT_WINDRES = 316; // windRes
	public final static int STAT_WATERRES = 317; // waterRes
	public final static int STAT_EARTHRES = 318; // earthRes
	public final static int STAT_HOLYRES = 336; // holyRes
	public final static int STAT_DARKRES = 337; // darkRes
	public final static int STAT_ROOTRES = 319; // rootRes
	public final static int STAT_SLEEPRES = 320; // sleepRes
	public final static int STAT_CONFUSIONRES = 321; // confusRes
	public final static int STAT_BREATH = 322; // breath
	public final static int STAT_AGGRESSION = 323; // aggr
	public final static int STAT_BLEED = 324; // bleed
	public final static int STAT_POISON = 325; // poison
	public final static int STAT_STUN = 326; // stun
	public final static int STAT_ROOT = 327; // root
	public final static int STAT_MOVEMENT = 328; // move
	public final static int STAT_EVASION = 329; // evas
	public final static int STAT_ACCURACY = 330; // accu
	public final static int STAT_COMBAT_STRENGTH = 331;
	public final static int STAT_COMBAT_WEAKNESS = 332;
	public final static int STAT_ATTACK_RANGE = 333; // rAtk
	public final static int STAT_NOAGG = 334; // noagg
	public final static int STAT_SHIELDDEF = 335; // sDef
	public final static int STAT_MP_CONSUME_RATE = 336; // Rate of mp consume
	// per skill use
	public final static int STAT_HP_CONSUME_RATE = 337; // Rate of hp consume
	// per skill use
	public final static int STAT_DANCE_MP_CONSUME_RATE = 338; // rate of mp
																// consume per
																// dance use
	public final static int STAT_MCRITRATE = 339; // Magic Crit Rate
	// COMBAT DAMAGE MODIFIER SKILLS...DETECT WEAKNESS AND WEAKNESS/STRENGTH
	public final static int COMBAT_MOD_ANIMAL = 200;
	public final static int COMBAT_MOD_BEAST = 201;
	public final static int COMBAT_MOD_BUG = 202;
	public final static int COMBAT_MOD_DRAGON = 203;
	public final static int COMBAT_MOD_MONSTER = 204;
	public final static int COMBAT_MOD_PLANT = 205;
	public final static int COMBAT_MOD_HOLY = 206;
	public final static int COMBAT_MOD_UNHOLY = 207;
	public final static int COMBAT_MOD_BOW = 208;
	public final static int COMBAT_MOD_BLUNT = 209;
	public final static int COMBAT_MOD_DAGGER = 210;
	public final static int COMBAT_MOD_FIST = 211;
	public final static int COMBAT_MOD_DUAL = 212;
	public final static int COMBAT_MOD_SWORD = 213;
	public final static int COMBAT_MOD_POISON = 214;
	public final static int COMBAT_MOD_BLEED = 215;
	public final static int COMBAT_MOD_FIRE = 216;
	public final static int COMBAT_MOD_WATER = 217;
	public final static int COMBAT_MOD_EARTH = 218;
	public final static int COMBAT_MOD_WIND = 219;
	public final static int COMBAT_MOD_ROOT = 220;
	public final static int COMBAT_MOD_STUN = 221;
	public final static int COMBAT_MOD_CONFUSION = 222;
	public final static int COMBAT_MOD_DARK = 223;
	// conditional values
	public final static int COND_RUNNING = 0x0001;
	public final static int COND_WALKING = 0x0002;
	public final static int COND_SIT = 0x0004;
	public final static int COND_BACK = 0x0008;
	public final static int COND_CRIT = 0x0010;
	public final static int COND_LOWHP = 0x0020;
	public final static int COND_ROBES = 0x0040;
	public final static int COND_CHARGES = 0x0080;
	public final static int COND_SHIELD = 0x0100;
	public final static int COND_GRADEA = 0x010000;
	public final static int COND_GRADEB = 0x020000;
	public final static int COND_GRADEC = 0x040000;
	public final static int COND_GRADED = 0x080000;
	public final static int COND_GRADES = 0x100000;
	private static final Func[] _emptyFunctionSet = new Func[0];
	private static final L2Effect[] _emptyEffectSet = new L2Effect[0];
	// these two build the primary key
	private final int _id;
	private final int _level;
	/** Identifier for a skill that client can't display */
	private int _displayId;
	// not needed, just for easier debug
	private final String _name;
	private final SkillOpType _operateType;
	private final boolean _magic;
	private final int _mpConsume;
	private final int _mpInitialConsume;
	private final int _hpConsume;
	private final int _targetConsume;
	private final int _targetConsumeId;
	private final int _itemConsume;
	private final int _itemConsumeId;
	// item consume count over time
	private final int _itemConsumeOT;
	// item consume id over time
	private final int _itemConsumeIdOT;
	// how many times to consume an item
	private final int _itemConsumeSteps;
	// for summon spells:
	// a) What is the total lifetime of summons (in millisecs)
	private final int _summonTotalLifeTime;
	// b) how much lifetime is lost per second of idleness (non-fighting)
	private final int _summonTimeLostIdle;
	// c) how much time is lost per second of activity (fighting)
	private final int _summonTimeLostActive;
	// item consume time in milliseconds
	private final int _itemConsumeTime;
	private final int _castRange;
	private final int _effectRange;
	// all times in milliseconds
	private final int _hitTime;
	// private final int _skillInterruptTime;
	private final int _coolTime;
	private final int _reuseDelay;
	private final int _buffDuration;
	/** Target type of the skill : SELF, PARTY, CLAN, PET... */
	private final SkillTargetType _targetType;
	private final double _power;
	private final int _effectPoints;
	private final int _magicLevel;
	private final String[] _negateStats;
	private final float _negatePower;
	private final int _negateId;
	private final int _levelDepend;
	// Effecting area of the skill, in radius.
	// The radius center varies according to the _targetType:
	// "caster" if targetType = AURA/PARTY/CLAN or "target" if targetType =
	// AREA
	private final int _skillRadius;
	private final SkillType _skillType;
	private final SkillType _effectType;
	private final int _effectPower;
	private final int _effectId;
	private final int _effectLvl;
	private final boolean _ispotion;
	private final int _element;
	private final int _savevs;
	private final boolean _isSuicideAttack;
	private final Stats _stat;
	private final int _condition;
	private final int _conditionValue;
	private final boolean _overhit;
	private final int _weaponsAllowed;
	private final int _armorsAllowed;
	private final int _addCrossLearn; // -1 disable, otherwice SP price for
	// others classes, default 1000
	private final float _mulCrossLearn; // multiplay for others classes, default
	// 2
	private final float _mulCrossLearnRace; // multiplay for others races,
	// default 2
	private final float _mulCrossLearnProf; // multiplay for fighter/mage
	// missmatch, default 3
	private final List<ClassId> _canLearn; // which classes can learn
	private final List<Integer> _teachers; // which NPC teaches
	private final int _minPledgeClass;
	private final boolean _isOffensive;
	private final int _numCharges;
	// OP Chance
	private final int _triggeredId;
	private final int _triggeredLevel;
	private final String _chanceType;
	protected ChanceCondition _chanceCondition = null;
	private final int _forceId;
	private final boolean _isHeroSkill; // If true the skill is a Hero Skill
	private final int _baseCritRate; // percent of success for skill critical
	// hit (especially for PDAM & BLOW -
	// they're not affected by rCrit values
	// or buffs). Default loads -1 for all
	// other skills but 0 to PDAM & BLOW
	private final int _lethalEffect1; // percent of success for lethal 1st
	// effect (hit cp to 1 or if mob hp to
	// 50%) (only for PDAM skills)
	private final int _lethalEffect2; // percent of success for lethal 2nd
	// effect (hit cp,hp to 1 or if mob hp
	// to 1) (only for PDAM skills)
	private final boolean _directHpDmg; // If true then dmg is being make
	// directly
	private final boolean _isDance; // If true then casting more dances will
	// cost more MP
	private final int _nextDanceCost;
	private final float _sSBoost; // If true skill will have SoulShot
	// boost
	// (power*2)
	private final int _aggroPoints;
	private final float _pvpMulti;
	/*
	 * Augmentation Skill Type 0 - Both 1 - Physical 2 - Magic 3 - Critical
	 * (unsupported now) 4 - You under attack (unsupported now)
	 */
	public final int augmentationType;
	protected Condition _preCondition;
	protected Condition _itemPreCondition;
	protected FuncTemplate[] _funcTemplates;
	protected EffectTemplate[] _effectTemplates;
	protected EffectTemplate[] _effectTemplatesSelf;

	protected L2Skill(StatsSet set) {
		_id = set.getInteger("skill_id");
		_level = set.getInteger("level");
		_displayId = set.getInteger("displayId", _id);
		_name = set.getString("name");
		_operateType = set.getEnum("operateType", SkillOpType.class);
		_magic = set.getBool("isMagic", false);
		_ispotion = set.getBool("isPotion", false);
		_mpConsume = set.getInteger("mpConsume", 0);
		_mpInitialConsume = set.getInteger("mpInitialConsume", 0);
		_hpConsume = set.getInteger("hpConsume", 0);
		_targetConsume = set.getInteger("targetConsumeCount", 0);
		_targetConsumeId = set.getInteger("targetConsumeId", 0);
		_itemConsume = set.getInteger("itemConsumeCount", 0);
		_itemConsumeId = set.getInteger("itemConsumeId", 0);
		_itemConsumeOT = set.getInteger("itemConsumeCountOT", 0);
		_itemConsumeIdOT = set.getInteger("itemConsumeIdOT", 0);
		_itemConsumeTime = set.getInteger("itemConsumeTime", 0);
		_itemConsumeSteps = set.getInteger("itemConsumeSteps", 0);
		_summonTotalLifeTime = set.getInteger("summonTotalLifeTime", 1200000); // 20
		// minutes
		// default
		_summonTimeLostIdle = set.getInteger("summonTimeLostIdle", 0);
		_summonTimeLostActive = set.getInteger("summonTimeLostActive", 0);
		_castRange = set.getInteger("castRange", 0);
		_effectRange = set.getInteger("effectRange", -1);
		_hitTime = set.getInteger("hitTime", 0);
		_coolTime = set.getInteger("coolTime", 0);
		// _skillInterruptTime = set.getInteger("hitTime", _hitTime / 2);
		_reuseDelay = set.getInteger("reuseDelay", 0);
		_buffDuration = set.getInteger("buffDuration", 0);
		_skillRadius = set.getInteger("skillRadius", 80);
		_targetType = set.getEnum("target", SkillTargetType.class);
		_power = set.getFloat("power", 0.f);
		_effectPoints = set.getInteger("effectPoints", 0);
		_negateStats = set.getString("negateStats", "").split(" ");
		_negatePower = set.getFloat("negatePower", 0.f);
		_negateId = set.getInteger("negateId", 0);
		_magicLevel = set.getInteger("magicLvl", SkillTreeTable.getInstance()
				.getMinSkillLevel(_id, _level));
		_levelDepend = set.getInteger("lvlDepend", 0);
		_stat = set.getEnum("stat", Stats.class, null);
		_skillType = set.getEnum("skillType", SkillType.class);
		_effectType = set.getEnum("effectType", SkillType.class, null);
		_effectPower = set.getInteger("effectPower", 0);
		_effectId = set.getInteger("effectId", 0);
		_effectLvl = set.getInteger("effectLevel", 0);
		_element = set.getInteger("element", 0);
		_savevs = set.getInteger("save", 0);
		_condition = set.getInteger("condition", 0);
		_conditionValue = set.getInteger("conditionValue", 0);
		_overhit = set.getBool("overHit", false);
		_isSuicideAttack = set.getBool("isSuicideAttack", false);
		_weaponsAllowed = set.getInteger("weaponsAllowed", 0);
		_armorsAllowed = set.getInteger("armorsAllowed", 0);
		_addCrossLearn = set.getInteger("addCrossLearn", 1000);
		_mulCrossLearn = set.getFloat("mulCrossLearn", 2.f);
		_mulCrossLearnRace = set.getFloat("mulCrossLearnRace", 2.f);
		_mulCrossLearnProf = set.getFloat("mulCrossLearnProf", 3.f);
		_minPledgeClass = set.getInteger("minPledgeClass", 0);
		_isOffensive = set.getBool("offensive", isSkillTypeOffensive());
		_numCharges = set.getInteger("num_charges", getLevel());
		_triggeredId = set.getInteger("triggeredId", 0);
		_triggeredLevel = set.getInteger("triggeredLevel", 0);
		_chanceType = set.getString("chanceType", "");
		if (_chanceType != "" && !_chanceType.isEmpty())
			_chanceCondition = ChanceCondition.parse(set);
		_forceId = set.getInteger("forceId", 0);
		_isHeroSkill = HeroSkillTable.isHeroSkill(_id);
		_baseCritRate = set.getInteger("baseCritRate", _skillType == SkillType.PDAM || _skillType == SkillType.BLOW ? 0 : -1);
		_lethalEffect1 = set.getInteger("lethal1", 0);
		_lethalEffect2 = set.getInteger("lethal2", 0);
		_directHpDmg = set.getBool("dmgDirectlyToHp", false);
		_isDance = set.getBool("isDance", false);
		_nextDanceCost = set.getInteger("nextDanceCost", 0);
		_sSBoost = set.getFloat("SSBoost", 0.f);
		_aggroPoints = set.getInteger("aggroPoints", 0);
		_pvpMulti = set.getFloat("pvpMulti", 1.f);
		// Augmentation Skill Type
		augmentationType = set.getInteger("augmentType", -1);
		String canLearn = set.getString("canLearn", null);
		if (canLearn == null)
			_canLearn = null;
		else {
			_canLearn = new FastList<ClassId>();
			StringTokenizer st = new StringTokenizer(canLearn, " \r\n\t,;");
			while (st.hasMoreTokens()) {
				String cls = st.nextToken();
				try {
					_canLearn.add(ClassId.valueOf(cls));
				} catch (Throwable t) {
					_log.log(Level.SEVERE, "Bad class " + cls
							+ " to learn skill", t);
				}
			}
		}
		String teachers = set.getString("teachers", null);
		if (teachers == null)
			_teachers = null;
		else {
			_teachers = new FastList<Integer>();
			StringTokenizer st = new StringTokenizer(teachers, " \r\n\t,;");
			while (st.hasMoreTokens()) {
				String npcid = st.nextToken();
				try {
					_teachers.add(Integer.parseInt(npcid));
				} catch (Throwable t) {
					_log.log(Level.SEVERE, "Bad teacher id " + npcid
							+ " to teach skill", t);
				}
			}
		}
	}

	public abstract void useSkill(L2Character caster, L2Object[] targets);

	public final boolean isPotion() {
		return _ispotion;
	}

	public final int getArmorsAllowed() {
		return _armorsAllowed;
	}

	public final int getConditionValue() {
		return _conditionValue;
	}

	public final SkillType getSkillType() {
		return _skillType;
	}

	public final int getSavevs() {
		return _savevs;
	}

	public final int getElement() {
		return _element;
	}

	/**
	 * Return the target type of the skill : SELF, PARTY, CLAN, PET...<BR>
	 * <BR>
	 */
	public final SkillTargetType getTargetType() {
		return _targetType;
	}

	public final int getCondition() {
		return _condition;
	}

	public final boolean isOverhit() {
		return _overhit;
	}

	public final boolean isSuicideAttack() {
		return _isSuicideAttack;
	}

	/**
	 * Return the power of the skill.<BR>
	 * <BR>
	 */
	public final double getPower(L2Character activeChar) {
		if (activeChar == null)
			return _power;
		switch (_skillType) {
		case DEATHLINK: {
			return _power
					* Math.pow(
							1.7165 - activeChar.getCurrentHp()
									/ activeChar.getMaxHp(), 2) * 0.577;
			/*
			 * DrHouse: Rolling back to old formula (look below) for DEATHLINK
			 * due to this one based on logarithm is not accurate enough.
			 * Commented here because probably is a matter of just adjusting a
			 * constant if(activeChar.getCurrentHp() / activeChar.getMaxHp() >
			 * 0.005) return
			 * _power*(-0.45*Math.log(activeChar.getCurrentHp()/activeChar
			 * .getMaxHp())+1.); else return _power*(-0.45*Math.log(0.005)+1.);
			 */
		}
		case FATALCOUNTER: {
			return _power * 3.5
					* (1 - activeChar.getCurrentHp() / activeChar.getMaxHp());
		}
		default:
			return _power;
		}
	}

	public final double getPower() {
		return _power;
	}

	public final int getEffectPoints() {
		return _effectPoints;
	}

	public final String[] getNegateStats() {
		return _negateStats;
	}

	public final float getNegatePower() {
		return _negatePower;
	}

	public final int getNegateId() {
		return _negateId;
	}

	public final int getMagicLevel() {
		return _magicLevel;
	}

	public final int getLevelDepend() {
		return _levelDepend;
	}

	/**
	 * Return the additional effect power or base probability.<BR>
	 * <BR>
	 */
	public final int getEffectPower() {
		return _effectPower;
	}

	/**
	 * Return the additional effect Id.<BR>
	 * <BR>
	 */
	public final int getEffectId() {
		return _effectId;
	}

	/**
	 * Return the additional effect level.<BR>
	 * <BR>
	 */
	public final int getEffectLvl() {
		return _effectLvl;
	}

	/**
	 * Return the additional effect skill type (ex : STUN, PARALYZE,...).<BR>
	 * <BR>
	 */
	public final SkillType getEffectType() {
		return _effectType;
	}

	/**
	 * @return Returns the buffDuration.
	 */
	public final int getBuffDuration() {
		return _buffDuration;
	}

	/**
	 * @return Returns the castRange.
	 */
	public final int getCastRange() {
		return _castRange;
	}

	/**
	 * @return Returns the effectRange.
	 */
	public final int getEffectRange() {
		return _effectRange;
	}

	/**
	 * @return Returns the hpConsume.
	 */
	public final int getHpConsume() {
		return _hpConsume;
	}

	/**
	 * @return Returns the id.
	 */
	public final int getId() {
		return _id;
	}

	public int getDisplayId() {
		return _displayId;
	}

	public void setDisplayId(int id) {
		_displayId = id;
	}

	public int getForceId() {
		return _forceId;
	}

	public int getTriggeredId() {
		return _triggeredId;
	}

	public int getTriggeredLevel() {
		return _triggeredLevel;
	}

	public boolean triggerAnotherSkill() {
		return _triggeredId > 1;
	}

	/**
	 * Return the skill type (ex : BLEED, SLEEP, WATER...).<BR>
	 * <BR>
	 */
	public final Stats getStat() {
		return _stat;
	}

	/**
	 * @return Returns the _targetConsumeId.
	 */
	public final int getTargetConsumeId() {
		return _targetConsumeId;
	}

	/**
	 * @return Returns the targetConsume.
	 */
	public final int getTargetConsume() {
		return _targetConsume;
	}

	/**
	 * @return Returns the itemConsume.
	 */
	public final int getItemConsume() {
		return _itemConsume;
	}

	/**
	 * @return Returns the itemConsumeId.
	 */
	public final int getItemConsumeId() {
		return _itemConsumeId;
	}

	/**
	 * @return Returns the itemConsume count over time.
	 */
	public final int getItemConsumeOT() {
		return _itemConsumeOT;
	}

	/**
	 * @return Returns the itemConsumeId over time.
	 */
	public final int getItemConsumeIdOT() {
		return _itemConsumeIdOT;
	}

	/**
	 * @return Returns the itemConsume count over time.
	 */
	public final int getItemConsumeSteps() {
		return _itemConsumeSteps;
	}

	/**
	 * @return Returns the itemConsume count over time.
	 */
	public final int getTotalLifeTime() {
		return _summonTotalLifeTime;
	}

	/**
	 * @return Returns the itemConsume count over time.
	 */
	public final int getTimeLostIdle() {
		return _summonTimeLostIdle;
	}

	/**
	 * @return Returns the itemConsumeId over time.
	 */
	public final int getTimeLostActive() {
		return _summonTimeLostActive;
	}

	/**
	 * @return Returns the itemConsume time in milliseconds.
	 */
	public final int getItemConsumeTime() {
		return _itemConsumeTime;
	}

	/**
	 * @return Returns the level.
	 */
	public final int getLevel() {
		return _level;
	}

	/**
	 * @return Returns the magic.
	 */
	public final boolean isMagic() {
		return _magic;
	}

	/**
	 * @return Returns the mpConsume.
	 */
	public final int getMpConsume() {
		return _mpConsume;
	}

	/**
	 * @return Returns the mpInitialConsume.
	 */
	public final int getMpInitialConsume() {
		return _mpInitialConsume;
	}

	/**
	 * @return Returns the name.
	 */
	public final String getName() {
		return _name;
	}

	/**
	 * @return Returns the reuseDelay.
	 */
	public final int getReuseDelay() {
		return _reuseDelay;
	}

	@Deprecated
	public final int getSkillTime() {
		return _hitTime;
	}

	public final int getHitTime() {
		return _hitTime;
	}

	/**
	 * @return Returns the coolTime.
	 */
	public final int getCoolTime() {
		return _coolTime;
	}

	public final int getSkillRadius() {
		return _skillRadius;
	}

	public final boolean isActive() 
	{
		return _operateType == SkillOpType.OP_ACTIVE;
	}

	public final boolean isPassive() 
	{
		return _operateType == SkillOpType.OP_PASSIVE;
	}

	public final boolean isToggle() 
	{
		return _operateType == SkillOpType.OP_TOGGLE;
	}

	public final boolean isChance() {
		return _chanceCondition != null && isPassive();
	}

	public boolean triggersChanceSkill() {
		return _triggeredId > 0 && isChance();
	}

	public int getTriggeredChanceId() {
		return _triggeredId;
	}

	public int getTriggeredChanceLevel() {
		return _triggeredLevel;
	}

	public ChanceCondition getTriggeredChanceCondition() {
		return _chanceCondition;
	}

	public ChanceCondition getChanceCondition() {
		return _chanceCondition;
	}

	public final boolean isDance() {
		return _isDance;
	}

	public final int getNextDanceMpCost() {
		return _nextDanceCost;
	}

	public final float getSSBoost() {
		return _sSBoost;
	}

	public final int getAggroPoints() {
		return _aggroPoints;
	}

	public final float getPvpMulti() {
		return _pvpMulti;
	}

	public final boolean useSoulShot() {
		switch (getSkillType()) {
		case PDAM:
		case STUN:
		case CHARGEDAM:
		case BLOW:
			return true;
		default:
			return false;
		}
	}

	public final boolean useSpiritShot() {
		return isMagic();
	}

	public final boolean useFishShot() {
		return getSkillType() == SkillType.PUMPING
				|| getSkillType() == SkillType.REELING;
	}

	public final int getWeaponsAllowed() {
		return _weaponsAllowed;
	}

	public final int getCrossLearnAdd() {
		return _addCrossLearn;
	}

	public final float getCrossLearnMul() {
		return _mulCrossLearn;
	}

	public final float getCrossLearnRace() {
		return _mulCrossLearnRace;
	}

	public final float getCrossLearnProf() {
		return _mulCrossLearnProf;
	}

	public final boolean getCanLearn(ClassId cls) {
		return _canLearn == null || _canLearn.contains(cls);
	}

	public final boolean canTeachBy(int npcId) {
		return _teachers == null || _teachers.contains(npcId);
	}

	public int getMinPledgeClass() {
		return _minPledgeClass;
	}

	public final boolean isPvpSkill() {
		switch (_skillType) {
		case DOT:
		case BLEED:
		case CONFUSION:
		case POISON:
		case DEBUFF:
		case AGGDEBUFF:
		case STUN:
		case ROOT:
		case FEAR:
		case SLEEP:
		case MDOT:
		case MUTE:
		case WEAKNESS:
		case PARALYZE:
		case CANCEL:
		case MAGE_BANE:
		case WARRIOR_BANE:
		case BETRAY:
		case FATALCOUNTER:
		case AGGREDUCE_CHAR:
		case AGGDAMAGE:
			return true;
		default:
			return false;
		}
	}

	public final boolean isOffensive() {
		return _isOffensive;
	}

	public final boolean isHeroSkill() {
		return _isHeroSkill;
	}

	public final int getNumCharges() {
		return _numCharges;
	}

	public final int getBaseCritRate() {
		return _baseCritRate;
	}

	public final int getLethalChance1() {
		return _lethalEffect1;
	}

	public final int getLethalChance2() {
		return _lethalEffect2;
	}

	public final boolean getDmgDirectlyToHP() {
		return _directHpDmg;
	}

	public final boolean isSkillTypeOffensive() {
		switch (_skillType) {
		case PDAM:
		case MDAM:
		case CPDAM:
		case DOT:
		case BLEED:
		case POISON:
		case AGGDAMAGE:
		case DEBUFF:
		case AGGDEBUFF:
		case STUN:
		case ROOT:
		case CONFUSION:
		case ERASE:
		case BLOW:
		case FEAR:
		case DRAIN:
		case SLEEP:
		case CHARGEDAM:
		case CONFUSE_MOB_ONLY:
		case DEATHLINK:
		case DETECT_WEAKNESS:
		case MANADAM:
		case MDOT:
		case MUTE:
		case SOULSHOT:
		case SPIRITSHOT:
		case SPOIL:
		case WEAKNESS:
		case MANA_BY_LEVEL:
		case SWEEP:
		case PARALYZE:
		case DRAIN_SOUL:
		case AGGREDUCE:
		case CANCEL:
		case MAGE_BANE:
		case WARRIOR_BANE:
		case AGGREMOVE:
		case AGGREDUCE_CHAR:
		case BETRAY:
		case DELUXE_KEY_UNLOCK:
		case FATALCOUNTER:
		case SOW:
		case HARVEST:
			return true;
		default:
			return false;
		}
	}

	public final boolean getWeaponDependancy(L2Character activeChar) {
		int weaponsAllowed = getWeaponsAllowed();
		// check to see if skill has a weapon dependency.
		if (weaponsAllowed == 0)
			return true;
		int mask = 0;
		if (activeChar.getActiveWeaponItem() != null)
			mask |= activeChar.getActiveWeaponItem().getItemType().mask();
		if (activeChar.getSecondaryWeaponItem() != null)
			mask |= activeChar.getSecondaryWeaponItem().getItemType().mask();
		if ((mask & weaponsAllowed) != 0)
			return true;
		return false;
	}

	public boolean checkCondition(L2Character activeChar, L2Object target,
			boolean itemOrWeapon) {
		if ((getCondition() & L2Skill.COND_SHIELD) != 0) {
			/*
			 * L2Armor armorPiece; L2ItemInstance dummy; dummy =
			 * activeChar.getInventory
			 * ().getPaperdollItem(Inventory.PAPERDOLL_RHAND); armorPiece =
			 * (L2Armor) dummy.getItem();
			 */
			// TODO add checks for shield here.
		}
		Condition preCondition = _preCondition;
		if (itemOrWeapon)
			preCondition = _itemPreCondition;
		if (preCondition == null)
			return true;
		Env env = new Env();
		env.player = activeChar;
		if (target instanceof L2Character)
			env.target = (L2Character) target;
		env.skill = this;
		if (!preCondition.test(env)) {
			String msg = preCondition.getMessage();
			if (msg != null) {
				SystemMessage sm = new SystemMessage(SystemMessageId.S1_S2);
				sm.addString(msg);
				activeChar.sendPacket(sm);
			}
			return false;
		}
		return true;
	}

	/**
	 * Return all targets of the skill in a table in function a the skill type.<BR>
	 * <BR>
	 * <B><U> Values of skill type</U> :</B><BR>
	 * <BR>
	 * <li>ONE : The skill can only be used on the L2PcInstance targeted, or on
	 * the caster if it's a L2PcInstance and no L2PcInstance targeted</li> <li>
	 * SELF</li> <li>HOLY, UNDEAD</li> <li>PET</li> <li>AURA, AURA_CLOSE</li>
	 * <li>AREA</li> <li>MULTIFACE</li> <li>PARTY, CLAN</li> <li>CORPSE_PLAYER,
	 * CORPSE_MOB, CORPSE_CLAN</li> <li>UNLOCKABLE</li> <li>ITEM</li> <BR>
	 * <BR>
	 * 
	 * @param activeChar
	 *            The L2Character who use the skill
	 */
	public final L2Object[] getTargetList(L2Character activeChar,boolean onlyFirst) 
	{
		List<L2Character> targetList = new FastList<L2Character>();
		// Get the target type of the skill
		// (ex : ONE, SELF, HOLY, PET, AURA, AURA_CLOSE, AREA, MULTIFACE, PARTY,
		// CLAN, CORPSE_PLAYER, CORPSE_MOB, CORPSE_CLAN, UNLOCKABLE, ITEM,
		// UNDEAD)
		SkillTargetType targetType = getTargetType();
		// Init to null the target of the skill
		L2Character target = null;
		// Get the L2Objcet targeted by the user of the skill at this moment
		L2Object objTarget = activeChar.getTarget();
		// Get the type of the skill
		// (ex : PDAM, MDAM, DOT, BLEED, POISON, HEAL, HOT, MANAHEAL,
		// MANARECHARGE, AGGDAMAGE, BUFF, DEBUFF, STUN, ROOT, RESURRECT,
		// PASSIVE...)
		SkillType skillType = getSkillType();
		// If the L2Object targeted is a L2Character, it becomes the L2Character
		// target
		if (objTarget instanceof L2Character)
			target = (L2Character) objTarget;
		switch (targetType) {
		// The skill can only be used on the L2Character targeted, or on the
		// caster itself
		case TARGET_ONE: 
		{
			boolean canTargetSelf = false;
			switch (skillType) 
			{
			case BUFF:
			case HEAL:
			case HOT:
			case HEAL_PERCENT:
			case MANARECHARGE:
			case MANAHEAL:
			case NEGATE:
			case REFLECT:
			case UNBLEED:
			case UNPOISON:
			case SEED:
			case COMBATPOINTHEAL:
			case MAGE_BANE:
			case WARRIOR_BANE:
			case BETRAY:
			case BALANCE_LIFE:
			case FORCE_BUFF:
			case GIVE_SP:
				canTargetSelf = true;
				break;
			case AGGDAMAGE:
				break;
			case AGGDEBUFF:
				break;
			case AGGREDUCE:
				break;
			case AGGREDUCE_CHAR:
				break;
			case AGGREMOVE:
				break;
			case BEAST_FEED:
				break;
			case BLEED:
				break;
			case BLOW:
				break;
			case CANCEL:
				break;
			case CHARGE:
				break;
			case CHARGEDAM:
				break;
			case COMBATPOINTPERHEAL:
				break;
			case COMMON_CRAFT:
				break;
			case CONFUSE_MOB_ONLY:
				break;
			case CONFUSION:
				break;
			case CONT:
				break;
			case CPDAM:
				break;
			case CPHOT:
				break;
			case CREATE_ITEM:
				break;
			case DEATHLINK:
				break;
			case DEATHLINK_PET:
				break;
			case DEBUFF:
				break;
			case DELUXE_KEY_UNLOCK:
				break;
			case DETECT_WEAKNESS:
				break;
			case DOT:
				break;
			case DRAIN:
				break;
			case DRAIN_SOUL:
				break;
			case DWARVEN_CRAFT:
				break;
			case ENCHANT_ARMOR:
				break;
			case ENCHANT_WEAPON:
				break;
			case ERASE:
				break;
			case EXITBUFFS:
				break;
			case FAKE_DEATH:
				break;
			case FATALCOUNTER:
				break;
			case FEAR:
				break;
			case FEED_PET:
				break;
			case FISHING:
				break;
			case GET_PLAYER:
				break;
			case HARVEST:
				break;
			case HEAL_STATIC:
				break;
			case LUCK:
				break;
			case MANADAM:
				break;
			case MANAHEAL_PERCENT:
				break;
			case MANA_BY_LEVEL:
				break;
			case MDAM:
				break;
			case MDOT:
				break;
			case MHOT:
				break;
			case MOG:
				break;
			case MPHOT:
				break;
			case MUTE:
				break;
			case NEEDCHARGE:
				break;
			case NOTDONE:
				break;
			case PARALYZE:
				break;
			case PASSIVE:
				break;
			case PDAM:
				break;
			case POISON:
				break;
			case PUMPING:
				break;
			case RECALL:
				break;
			case REELING:
				break;
			case RESURRECT:
				break;
			case ROOT:
				break;
			case SIEGEFLAG:
				break;
			case SIGNET:
				break;
			case SIGNET_CASTTIME:
				break;
			case SLEEP:
				break;
			case SOULSHOT:
				break;
			case SOW:
				break;
			case SPIRITSHOT:
				break;
			case SPOIL:
				break;
			case STRSIEGEASSAULT:
				break;
			case STUN:
				break;
			case SUMMON:
				break;
			case SUMMON_FRIEND:
				break;
			case SUMMON_TREASURE_KEY:
				break;
			case SWEEP:
				break;
			case TAKECASTLE:
				break;
			case UNDEAD_DEFENSE:
				break;
			case UNLOCK:
				break;
			case WEAKNESS:
				break;
			case WEAPON_SA:
				break;
			case ZAKENPLAYER:
				break;
			case ZAKENSELF:
				break;
			default:
				break;
			}
			// Check for null target or any other invalid target
			if (target == null || target.isDead() || target == activeChar
					&& !canTargetSelf) {
				activeChar.sendPacket(new SystemMessage(
						SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
			// If a target is found, return it in a table else send a system
			// message TARGET_IS_INCORRECT
			return new L2Character[] { target };
		}
		case TARGET_SELF:
		case TARGET_GROUND: {
			return new L2Character[] { activeChar };
		}
		case TARGET_HOLY: {
			if (activeChar instanceof L2PcInstance)
				if (activeChar.getTarget() instanceof L2ArtefactInstance)
					return new L2Character[] { (L2ArtefactInstance) activeChar
							.getTarget() };
				else if (((L2PcInstance) activeChar).checkFOS())
					return new L2Character[] { (L2NpcInstance) activeChar
							.getTarget() };
			return null;
		}
		case TARGET_COUPLE: {
			if (target != null && target instanceof L2PcInstance) {
				int _chaid = activeChar.getObjectId();
				int targetId = target.getObjectId();
				for (Couple cl : CoupleManager.getInstance().getCouples())
					if (cl.getPlayer1Id() == _chaid
							&& cl.getPlayer2Id() == targetId
							|| cl.getPlayer2Id() == _chaid
							&& cl.getPlayer1Id() == targetId)
						return new L2Character[] { target };
			}
			return null;
		}
		case TARGET_PET: {
			target = activeChar.getPet();
			if (target != null && !target.isDead())
				return new L2Character[] { target };
			return null;
		}
		case TARGET_OWNER_PET: 
		{
			if (activeChar instanceof L2Summon) 
			{
				target = ((L2Summon) activeChar).getOwner();
				if (target != null && !target.isDead())
					return new L2Character[] { target };
			}
			return null;
		}
		case TARGET_CORPSE_PET: 
		{
			if (activeChar instanceof L2PcInstance) 
			{
				target = activeChar.getPet();
				if (target != null && target.isDead())
					return new L2Character[] { target };
			}
			return null;
		}
		case TARGET_AURA:
        case TARGET_FRONT_AURA:
        case TARGET_BEHIND_AURA:
        {
            final boolean srcInArena = (activeChar.isInsideZone(L2Character.ZONE_PVP) && !activeChar.isInsideZone(L2Character.ZONE_SIEGE));

			// Разрешаем сливаться на олимпе, но ни на кого действовать не будет
			if (activeChar.getActingPlayer().isInOlympiadMode() && !activeChar.getActingPlayer().isOlympiadStart())
			{
				return targetList.toArray(new L2Character[targetList.size()]);
			}
            // Go through the L2Character _knownList
            final Collection<L2Character> objs = activeChar.getKnownList().getKnownCharactersInRadius(getSkillRadius());
            //synchronized (activeChar.getKnownList().getKnownObjects())
			for (L2Character obj : objs)
			{
				if (obj instanceof L2Attackable || obj instanceof L2PlayableInstance)
				{
					switch (targetType)
					{
						case TARGET_FRONT_AURA:
							if (!obj.isInFrontOf(activeChar))
								continue;
							break;
						case TARGET_BEHIND_AURA:
							if(!obj.isBehind(activeChar))
								continue;
							break;
					default:
						break;
					}

					if (!checkForAreaOffensiveSkills(activeChar, obj, this, srcInArena))
						continue;

					targetList.add(obj);

					if (onlyFirst)
						return targetList.toArray(new L2Character[targetList.size()]);
				}
			}
            return targetList.toArray(new L2Character[targetList.size()]);
        }
		case TARGET_AREA: {
			if (!(target instanceof L2Attackable || target instanceof L2PlayableInstance)
					|| // Target
					getCastRange() >= 0
					&& (target == null || target == activeChar
							|| target.isDead() || target.isAlikeDead())) // target
			// is
			// null
			// or
			// self
			// or
			// dead/faking
			{
				activeChar.sendPacket(new SystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
			L2Character cha;
			if (getCastRange() >= 0) 
			{
				cha = target;
				if (!onlyFirst)
					targetList.add(cha); // Add target to target list
				else
					return new L2Character[] { cha };
			} else
				cha = activeChar;
			boolean effectOriginIsL2PlayableInstance = cha instanceof L2PlayableInstance;
			L2PcInstance src = null;
			if (activeChar instanceof L2PcInstance)
				src = (L2PcInstance) activeChar;
			else if (activeChar instanceof L2Summon)
				src = ((L2Summon) activeChar).getOwner();
			int radius = getSkillRadius();
			boolean srcInArena = activeChar.isInsideZone(L2Character.ZONE_PVP)
					&& !activeChar.isInsideZone(L2Character.ZONE_SIEGE);
			for (L2Object obj : activeChar.getKnownList().getKnownObjects()
					.values()) {
				if (obj == null)
					continue;
				if (!(obj instanceof L2Attackable || obj instanceof L2PlayableInstance))
					continue;
				if (obj == cha)
					continue;
				target = (L2Character) obj;
				if (!GeoData.getInstance().canSeeTarget(activeChar, target))
					continue;
				if (!target.isAlikeDead() && target != activeChar) {
					if (!Util.checkIfInRange(radius, obj, cha, true))
						continue;
					if (src != null) // caster is l2playableinstance and
					// exists
					{
						if (obj instanceof L2PcInstance) {
							L2PcInstance trg = (L2PcInstance) obj;
							if (trg == src)
								continue;
							if (src.getParty() != null
									&& trg.getParty() != null
									&& src.getParty().getPartyLeaderOID() == trg
											.getParty().getPartyLeaderOID())
								continue;
							if (trg.isInsideZone(L2Character.ZONE_PEACE))
								continue;
							if (!srcInArena
									&& !(trg.isInsideZone(L2Character.ZONE_PVP) && !trg
											.isInsideZone(L2Character.ZONE_SIEGE))) {
								if (src.getAllyId() == trg.getAllyId()
										&& src.getAllyId() != 0)
									continue;
								if (src.getClan() != null
										&& trg.getClan() != null)
									if (src.getClan().getClanId() == trg
											.getClan().getClanId())
										continue;
								if (!src.checkPvpSkill(obj, this))
									continue;
							}
						}
						if (obj instanceof L2Summon) {
							L2PcInstance trg = ((L2Summon) obj).getOwner();
							if (trg == src)
								continue;
							if (src.getParty() != null
									&& trg.getParty() != null
									&& src.getParty().getPartyLeaderOID() == trg
											.getParty().getPartyLeaderOID())
								continue;
							if (!srcInArena
									&& !(trg.isInsideZone(L2Character.ZONE_PVP) && !trg
											.isInsideZone(L2Character.ZONE_SIEGE))) {
								if (src.getAllyId() == trg.getAllyId()
										&& src.getAllyId() != 0)
									continue;
								if (src.getClan() != null
										&& trg.getClan() != null)
									if (src.getClan().getClanId() == trg
											.getClan().getClanId())
										continue;
								if (!src.checkPvpSkill(trg, this))
									continue;
							}
							if (((L2Summon) obj)
									.isInsideZone(L2Character.ZONE_PEACE))
								continue;
							continue;
						}
					} else if (effectOriginIsL2PlayableInstance && // If effect
							// starts at
							// L2PlayableInstance
							// and
							!(obj instanceof L2PlayableInstance))
						// is
						// not
						// L2PlayableInstance
						continue;
					targetList.add((L2Character) obj);
				}
			}
			if (targetList.size() == 0)
				return null;
			return targetList.toArray(new L2Character[targetList.size()]);
		}
		case TARGET_MULTIFACE: {
			if (!(target instanceof L2Attackable)
					&& !(target instanceof L2PcInstance)) {
				activeChar.sendPacket(new SystemMessage(
						SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
			if (onlyFirst == false)
				targetList.add(target);
			else
				return new L2Character[] { target };
			int radius = getSkillRadius();
			for (L2Object obj : activeChar.getKnownList().getKnownObjects()
					.values()) {
				if (obj == null)
					continue;
				if (!Util.checkIfInRange(radius, activeChar, obj, true))
					continue;
				if (obj instanceof L2Attackable && obj != target)
					targetList.add((L2Character) obj);
				if (targetList.size() == 0) {
					activeChar.sendPacket(new SystemMessage(
							SystemMessageId.TARGET_CANT_FOUND));
					return null;
				}
			}
			return targetList.toArray(new L2Character[targetList.size()]);
			// TODO multiface targets all around right now. need it to just
			// get targets
			// the character is facing.
		}
		case TARGET_PARTY: {
			if (onlyFirst)
				return new L2Character[] { activeChar };
			targetList.add(activeChar);
			L2PcInstance player = null;
			if (activeChar instanceof L2Summon) {
				player = ((L2Summon) activeChar).getOwner();
				targetList.add(player);
			} else if (activeChar instanceof L2PcInstance) {
				player = (L2PcInstance) activeChar;
				if (activeChar.getPet() != null)
					targetList.add(activeChar.getPet());
			}
			if (activeChar.getParty() != null) {
				// Get all visible objects in a spheric area near the
				// L2Character
				// Get a list of Party Members
				List<L2PcInstance> partyList = activeChar.getParty()
						.getPartyMembers();
				for (L2PcInstance partyMember : partyList) {
					if (partyMember == null)
						continue;
					if (partyMember == player)
						continue;
					if (!partyMember.isDead()
							&& Util.checkIfInRange(getSkillRadius(),
									activeChar, partyMember, true)) {
						targetList.add(partyMember);
						if (partyMember.getPet() != null
								&& !partyMember.getPet().isDead())
							targetList.add(partyMember.getPet());
					}
				}
			}
			return targetList.toArray(new L2Character[targetList.size()]);
		}
		case TARGET_PARTY_MEMBER: {
			if (target != null
					&& target == activeChar
					|| target != null
					&& activeChar.getParty() != null
					&& target.getParty() != null
					&& activeChar.getParty().getPartyLeaderOID() == target
							.getParty().getPartyLeaderOID() || target != null
					&& activeChar instanceof L2PcInstance
					&& target instanceof L2Summon
					&& activeChar.getPet() == target || target != null
					&& activeChar instanceof L2Summon
					&& target instanceof L2PcInstance
					&& activeChar == target.getPet()) {
				if (!target.isDead())
					// If a target is found, return it in a table else send
					// a system message TARGET_IS_INCORRECT
					return new L2Character[] { target };
				else
					return null;
			} else {
				activeChar.sendPacket(new SystemMessage(
						SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
		}
		case TARGET_PARTY_OTHER: {
			if (target != null
					&& target != activeChar
					&& activeChar.getParty() != null
					&& target.getParty() != null
					&& activeChar.getParty().getPartyLeaderOID() == target
							.getParty().getPartyLeaderOID()) {
				if (!target.isDead()) {
					if (target instanceof L2PcInstance) {
						L2PcInstance player = (L2PcInstance) target;
						switch (getId()) {
						// FORCE BUFFS may cancel here but there should be a
						// proper condition
						case 426:
							if (!player.isMageClass())
								return new L2Character[] { target };
							else
								return null;
						case 427:
							if (player.isMageClass())
								return new L2Character[] { target };
							else
								return null;
						}
					}
					return new L2Character[] { target };
				} else
					return null;
			} else {
				activeChar.sendPacket(new SystemMessage(
						SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
		}
		case TARGET_CORPSE_ALLY:
		case TARGET_ALLY: {
			if (activeChar instanceof L2PcInstance) {
				int radius = getSkillRadius();
				L2PcInstance player = (L2PcInstance) activeChar;
				L2Clan clan = player.getClan();
				if (player.isInOlympiadMode())
					return new L2Character[] { player };
				if (targetType != SkillTargetType.TARGET_CORPSE_ALLY)
					if (onlyFirst == false)
						targetList.add(player);
					else
						return new L2Character[] { player };
				if (clan != null)
					// Get all visible objects in a spheric area near the
					// L2Character
					// Get Clan Members
					for (L2Object newTarget : activeChar.getKnownList()
							.getKnownObjects().values()) {
						if (newTarget == null
								|| !(newTarget instanceof L2PcInstance))
							continue;
						if ((((L2PcInstance) newTarget).getAllyId() == 0 || ((L2PcInstance) newTarget)
								.getAllyId() != player.getAllyId())
								&& (((L2PcInstance) newTarget).getClan() == null || ((L2PcInstance) newTarget)
										.getClanId() != player.getClanId()))
							continue;
						if (player.isInDuel()
								&& (player.getDuelId() != ((L2PcInstance) newTarget)
										.getDuelId() || player.getParty() != null
										&& !player.getParty().getPartyMembers()
												.contains(newTarget)))
							continue;
						if (targetType == SkillTargetType.TARGET_CORPSE_ALLY) {
							if (!((L2PcInstance) newTarget).isDead())
								continue;
							if (getSkillType() == SkillType.RESURRECT)
								// check target is not in a active siege
								// zone
								if (((L2PcInstance) newTarget)
										.isInsideZone(L2Character.ZONE_SIEGE))
									continue;
						}
						if (!Util.checkIfInRange(radius, activeChar, newTarget,
								true))
							continue;
						// Don't add this target if this is a Pc->Pc pvp
						// casting and pvp condition not met
						if (!player.checkPvpSkill(newTarget, this))
							continue;
						if (onlyFirst == false)
							targetList.add((L2Character) newTarget);
						else
							return new L2Character[] { (L2Character) newTarget };
					}
			}
			return targetList.toArray(new L2Character[targetList.size()]);
		}
		case TARGET_ENEMY_ALLY: {
			// int charX, charY, charZ, targetX, targetY, targetZ, dx, dy, dz;
			int radius = getSkillRadius();
			L2Character newTarget;

			if (getCastRange() > -1 && target != null)
				newTarget = target;
			else
				newTarget = activeChar;

			if (newTarget != activeChar || isSkillTypeOffensive())
				targetList.add(newTarget);

			for (L2Character obj : activeChar.getKnownList()
					.getKnownCharactersInRadius(radius)) {
				if (obj == newTarget || obj == activeChar)
					continue;

				if (obj instanceof L2Attackable)
					if (!obj.isAlikeDead()) {
						// Don't add this target if this is a PC->PC pvp casting
						// and pvp condition not met
						if (activeChar instanceof L2PcInstance
								&& !((L2PcInstance) activeChar).checkPvpSkill(
										obj, this))
							continue;

						// check if both attacker and target are L2PcInstances
						// and if they are in same party or clan
						if (activeChar instanceof L2PcInstance
								&& obj instanceof L2PcInstance
								&& (((L2PcInstance) activeChar).getClanId() != ((L2PcInstance) obj)
										.getClanId() || ((L2PcInstance) activeChar)
										.getAllyId() != ((L2PcInstance) obj)
										.getAllyId()
										&& ((L2PcInstance) activeChar)
												.getParty() != null
										&& ((L2PcInstance) obj).getParty() != null
										&& ((L2PcInstance) activeChar)
												.getParty().getPartyLeaderOID() != ((L2PcInstance) obj)
												.getParty().getPartyLeaderOID()))
							continue;

						targetList.add(obj);
					}
			}
		}
		case TARGET_CORPSE_CLAN:
		case TARGET_CLAN: {
			if (activeChar instanceof L2PlayableInstance) {
				int radius = getSkillRadius();
				L2PcInstance player = null;
				if (activeChar instanceof L2Summon)
					player = ((L2Summon) activeChar).getOwner();
				else
					player = (L2PcInstance) activeChar;
				if (player == null)
					return null;
				L2Clan clan = player.getClan();
				if (player.isInOlympiadMode())
					return new L2Character[] { player };
				if (targetType != SkillTargetType.TARGET_CORPSE_CLAN)
					if (onlyFirst == false)
						targetList.add(player);
					else
						return new L2Character[] { player };
				if (clan != null)
					// Get all visible objects in a spheric area near the
					// L2Character
					// Get Clan Members
					for (L2ClanMember member : clan.getMembers()) {
						L2PcInstance newTarget = member.getPlayerInstance();
						if (newTarget == null)
							continue;
						if (targetType == SkillTargetType.TARGET_CORPSE_CLAN) {
							if (!newTarget.isDead())
								continue;
							if (getSkillType() == SkillType.RESURRECT)
								// check target is not in a active siege
								// zone
								if (newTarget
										.isInsideZone(L2Character.ZONE_SIEGE))
									continue;
						}
						if (player.isInDuel()
								&& (player.getDuelId() != newTarget.getDuelId() || player
										.getParty() != null
										&& !player.getParty().getPartyMembers()
												.contains(newTarget)))
							continue;
						if (!Util.checkIfInRange(radius, activeChar, newTarget,
								true))
							continue;
						// Don't add this target if this is a Pc->Pc pvp
						// casting and pvp condition not met
						if (!player.checkPvpSkill(newTarget, this))
							continue;
						if (onlyFirst == false)
							targetList.add(newTarget);
						else
							return new L2Character[] { newTarget };
					}
			}
			return targetList.toArray(new L2Character[targetList.size()]);
		}
		case TARGET_CORPSE_PLAYER: {
			if (target != null && target.isDead()) {
				L2PcInstance player = null;
				if (activeChar instanceof L2PcInstance)
					player = (L2PcInstance) activeChar;
				L2PcInstance targetPlayer = null;
				if (target instanceof L2PcInstance)
					targetPlayer = (L2PcInstance) target;
				L2PetInstance targetPet = null;
				if (target instanceof L2PetInstance)
					targetPet = (L2PetInstance) target;
				if (player != null
						&& (targetPlayer != null || targetPet != null)) {
					boolean condGood = true;
					if (getSkillType() == SkillType.RESURRECT) {
						// check target is not in a active siege zone
						if (target.isInsideZone(L2Character.ZONE_SIEGE)) {
							condGood = false;
							player.sendPacket(new SystemMessage(
									SystemMessageId.CANNOT_BE_RESURRECTED_DURING_SIEGE));
						}
						if (targetPlayer != null) {
							if (targetPlayer.isReviveRequested()) {
								if (targetPlayer.isRevivingPet())
									player.sendPacket(new SystemMessage(
											SystemMessageId.MASTER_CANNOT_RES)); // While
								// a
								// pet
								// is
								// attempting
								// to
								// resurrect,
								// it
								// cannot
								// help
								// in
								// resurrecting
								// its
								// master.
								else
									player.sendPacket(new SystemMessage(
											SystemMessageId.RES_HAS_ALREADY_BEEN_PROPOSED)); // Resurrection
								// is
								// already
								// been
								// proposed.
								condGood = false;
							}
						} else if (targetPet != null)
							if (targetPet.getOwner() != player) {
								condGood = false;
								player.sendMessage("You are not the owner of this pet");
							}
					}
					if (condGood)
						if (onlyFirst == false) {
							targetList.add(target);
							return targetList.toArray(new L2Object[targetList
									.size()]);
						} else
							return new L2Character[] { target };
				}
			}
			activeChar.sendPacket(new SystemMessage(
					SystemMessageId.TARGET_IS_INCORRECT));
			return null;
		}
		case TARGET_CORPSE_MOB: {
			if (!(target instanceof L2Attackable) || !target.isDead()) {
				activeChar.sendPacket(new SystemMessage(
						SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
			if (onlyFirst == false) {
				targetList.add(target);
				return targetList.toArray(new L2Object[targetList.size()]);
			} else
				return new L2Character[] { target };
		}
		case TARGET_AREA_CORPSE_MOB: {
			if (!(target instanceof L2Attackable) || !target.isDead()) {
				activeChar.sendPacket(new SystemMessage(
						SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
			if (onlyFirst == false)
				targetList.add(target);
			else
				return new L2Character[] { target };
			boolean srcInArena = activeChar.isInsideZone(L2Character.ZONE_PVP)
					&& !activeChar.isInsideZone(L2Character.ZONE_SIEGE);
			L2PcInstance src = null;
			if (activeChar instanceof L2PcInstance)
				src = (L2PcInstance) activeChar;
			L2PcInstance trg = null;
			int radius = getSkillRadius();
			if (activeChar.getKnownList() != null)
				for (L2Object obj : activeChar.getKnownList().getKnownObjects()
						.values()) {
					if (obj == null)
						continue;
					if (!(obj instanceof L2Attackable || obj instanceof L2PlayableInstance)
							|| ((L2Character) obj).isDead()
							|| (L2Character) obj == activeChar)
						continue;
					if (!Util.checkIfInRange(radius, target, obj, true))
						continue;
					if (!GeoData.getInstance().canSeeTarget(activeChar, obj))
						continue;
					if (obj instanceof L2PcInstance && src != null) {
						trg = (L2PcInstance) obj;
						if (src.getParty() != null
								&& trg.getParty() != null
								&& src.getParty().getPartyLeaderOID() == trg
										.getParty().getPartyLeaderOID())
							continue;
						if (trg.isInsideZone(L2Character.ZONE_PEACE))
							continue;
						if (!srcInArena
								&& !(trg.isInsideZone(L2Character.ZONE_PVP) && !trg
										.isInsideZone(L2Character.ZONE_SIEGE))) {
							if (src.getAllyId() == trg.getAllyId()
									&& src.getAllyId() != 0)
								continue;
							if (src.getClan() != null && trg.getClan() != null)
								if (src.getClan().getClanId() == trg.getClan()
										.getClanId())
									continue;
							if (!src.checkPvpSkill(obj, this))
								continue;
						}
					}
					if (obj instanceof L2Summon && src != null) {
						trg = ((L2Summon) obj).getOwner();
						if (src.getParty() != null
								&& trg.getParty() != null
								&& src.getParty().getPartyLeaderOID() == trg
										.getParty().getPartyLeaderOID())
							continue;
						if (!srcInArena
								&& !(trg.isInsideZone(L2Character.ZONE_PVP) && !trg
										.isInsideZone(L2Character.ZONE_SIEGE))) {
							if (src.getAllyId() == trg.getAllyId()
									&& src.getAllyId() != 0)
								continue;
							if (src.getClan() != null && trg.getClan() != null)
								if (src.getClan().getClanId() == trg.getClan()
										.getClanId())
									continue;
							if (!src.checkPvpSkill(trg, this))
								continue;
						}
						if (((L2Summon) obj)
								.isInsideZone(L2Character.ZONE_PEACE))
							continue;
						continue;
					}
					targetList.add((L2Character) obj);
				}
			if (targetList.size() == 0)
				return null;
			return targetList.toArray(new L2Character[targetList.size()]);
		}
		case TARGET_UNLOCKABLE: {
			if (!(target instanceof L2DoorInstance)
					&& !(target instanceof L2ChestInstance))
				// activeChar.sendPacket(new
				// SystemMessage(SystemMessage.TARGET_IS_INCORRECT));
				return null;
			if (onlyFirst == false) {
				targetList.add(target);
				return targetList.toArray(new L2Object[targetList.size()]);
			} else
				return new L2Character[] { target };
		}
		case TARGET_ITEM: {
			SystemMessage sm = new SystemMessage(SystemMessageId.S1_S2);
			sm.addString("Target type of skill is not currently handled");
			activeChar.sendPacket(sm);
			return null;
		}
		case TARGET_UNDEAD: {
			if (target instanceof L2NpcInstance
					|| target instanceof L2SummonInstance) {
				if (!target.isUndead() || target.isDead()) {
					activeChar.sendPacket(new SystemMessage(
							SystemMessageId.TARGET_IS_INCORRECT));
					return null;
				}
				if (onlyFirst == false)
					targetList.add(target);
				else
					return new L2Character[] { target };
				return targetList.toArray(new L2Object[targetList.size()]);
			} else {
				activeChar.sendPacket(new SystemMessage(
						SystemMessageId.TARGET_IS_INCORRECT));
				return null;
			}
		}
		case TARGET_AREA_UNDEAD: {
			L2Character cha;
			int radius = getSkillRadius();
			if (getCastRange() >= 0
					&& (target instanceof L2NpcInstance || target instanceof L2SummonInstance)
					&& target.isUndead() && !target.isAlikeDead()) {
				cha = target;
				if (onlyFirst == false)
					targetList.add(cha); // Add target to target list
				else
					return new L2Character[] { cha };
			} else
				cha = activeChar;
			if (cha != null && cha.getKnownList() != null)
				for (L2Object obj : cha.getKnownList().getKnownObjects()
						.values()) {
					if (obj == null)
						continue;
					if (obj instanceof L2NpcInstance)
						target = (L2NpcInstance) obj;
					else if (obj instanceof L2SummonInstance)
						target = (L2SummonInstance) obj;
					else
						continue;
					if (!GeoData.getInstance().canSeeTarget(activeChar, target))
						continue;
					if (!target.isAlikeDead()) // If target is not
					// dead/fake death and not
					// self
					{
						if (!target.isUndead())
							continue;
						if (!Util.checkIfInRange(radius, cha, obj, true))
							// to
							// next
							// obj
							// if
							// obj
							// isn't
							// in
							// range
							continue;
						if (onlyFirst == false)
							targetList.add((L2Character) obj); // Add obj
						// to target
						// lists
						else
							return new L2Character[] { (L2Character) obj };
					}
				}
			if (targetList.size() == 0)
				return null;
			return targetList.toArray(new L2Character[targetList.size()]);
		}
		case TARGET_ENEMY_SUMMON: {
			if (target != null && target instanceof L2Summon) {
				L2Summon targetSummon = (L2Summon) target;
				if (activeChar instanceof L2PcInstance
						&& activeChar.getPet() != targetSummon
						&& !targetSummon.isDead()
						&& (targetSummon.getOwner().getPvpFlag() != 0 || targetSummon
								.getOwner().getKarma() > 0)
						|| targetSummon.getOwner().isInsideZone(
								L2Character.ZONE_PVP)
						&& ((L2PcInstance) activeChar)
								.isInsideZone(L2Character.ZONE_PVP))
					return new L2Character[] { targetSummon };
			}
			return null;
		}
		case TARGET_PIG: {
			if (target != null && target instanceof L2NpcInstance) {
				int npcId = ((L2NpcInstance) target).getNpcId();
				if (npcId >= 13031 && npcId <= 13035)
					return new L2Character[] { target };
			}
			return null;
		}
		default: 
		{
			SystemMessage sm = new SystemMessage(SystemMessageId.S1_S2);
			sm.addString("Target type of skill is not currently handled");
			activeChar.sendPacket(sm);
			return null;
		}
		}// end switch
	}

	public final L2Object[] getTargetList(L2Character activeChar) {
		return getTargetList(activeChar, false);
	}

	public final L2Object getFirstOfTargetList(L2Character activeChar) {
		L2Object[] targets;
		targets = getTargetList(activeChar, true);
		if (targets == null || targets.length == 0)
			return null;
		else
			return targets[0];
	}
	
	public static boolean checkForAreaOffensiveSkills(L2Character caster, L2Character target, L2Skill skill, boolean sourceInArena)
    {
    	if (target == null || target.isDead() || target == caster)
    		return false;

    	final L2PcInstance player = caster.getActingPlayer();
    	if (player != null)
    	{
    		final L2PcInstance targetPlayer = target.getActingPlayer();
    		if (targetPlayer != null)
    		{
    			if (targetPlayer == caster || targetPlayer == player)
    				return false;

    			if (targetPlayer.inObserverMode())
    				return false;

    			if (target.isInsideZone(L2Character.ZONE_PEACE))
    				return false;

    			if (player.isInParty() && targetPlayer.isInParty())
    			{
    				// Same party
    				if (player.getParty().getPartyLeaderOID() == targetPlayer.getParty().getPartyLeaderOID())
    					return false;

    				// Same commandchannel
    				if (player.getParty().getCommandChannel() != null
    						&& player.getParty().getCommandChannel() == targetPlayer.getParty().getCommandChannel())
    					return false;
    			}

    			if (!sourceInArena && !(targetPlayer.isInsideZone(L2Character.ZONE_PVP) && !targetPlayer.isInsideZone(L2Character.ZONE_SIEGE)))
    			{
    				if (player.getAllyId() != 0 && player.getAllyId() == targetPlayer.getAllyId())
    					return false;

    				if (player.getClanId() != 0 && player.getClanId() == targetPlayer.getClanId())
    					return false;

    				if (!player.checkPvpSkill(targetPlayer, skill))
    					return false;
    			}
    		}
    	}
    	else
    	{
    		// source is not playable
    		if (!(target instanceof L2PlayableInstance))
    				return false;
    	}

    	//if (!GeoEngine.canSeeTarget(caster, target, caster.isFlying()))
		//	return false;

		return true;
    }

	public final Func[] getStatFuncs(L2Effect effect, L2Character player) {
		if (!(player instanceof L2PcInstance)
				&& !(player instanceof L2Attackable)
				&& !(player instanceof L2Summon))
			return _emptyFunctionSet;
		if (_funcTemplates == null)
			return _emptyFunctionSet;
		List<Func> funcs = new FastList<Func>();
		for (FuncTemplate t : _funcTemplates) {
			Env env = new Env();
			env.player = player;
			env.skill = this;
			Func f = t.getFunc(env, this); // skill is owner
			if (f != null)
				funcs.add(f);
		}
		if (funcs.size() == 0)
			return _emptyFunctionSet;
		return funcs.toArray(new Func[funcs.size()]);
	}

	public boolean hasEffects() {
		return _effectTemplates != null && _effectTemplates.length > 0;
	}

	public final L2Effect[] getEffects(L2Character effector,
			L2Character effected) {
		if (isPassive())
			return _emptyEffectSet;
		if (_effectTemplates == null)
			return _emptyEffectSet;
		if (effector != effected && effected.isInvul())
			return _emptyEffectSet;
		List<L2Effect> effects = new FastList<L2Effect>();
		for (EffectTemplate et : _effectTemplates) {
			Env env = new Env();
			env.player = effector;
			env.target = effected;
			env.skill = this;
			L2Effect e = et.getEffect(env);
			if (e != null)
				effects.add(e);
		}
		if (effects.size() == 0)
			return _emptyEffectSet;
		return effects.toArray(new L2Effect[effects.size()]);
	}

	public final L2Effect[] getEffectsSelf(L2Character effector) {
		if (isPassive())
			return _emptyEffectSet;
		if (_effectTemplatesSelf == null)
			return _emptyEffectSet;
		List<L2Effect> effects = new FastList<L2Effect>();
		for (EffectTemplate et : _effectTemplatesSelf) {
			Env env = new Env();
			env.player = effector;
			env.target = effector;
			env.skill = this;
			L2Effect e = et.getEffect(env);
			if (e != null)
				// Implements effect charge
				if (e.getEffectType() == L2Effect.EffectType.CHARGE) {
					env.skill = SkillTable.getInstance().getInfo(8,
							effector.getSkillLevel(8));
					EffectCharge effect = (EffectCharge) env.target
							.getFirstEffect(L2Effect.EffectType.CHARGE);
					if (effect != null) {
						int effectcharge = effect.getLevel();
						if (effectcharge < _numCharges) {
							effectcharge++;
							effect.addNumCharges(effectcharge);
							if (env.target instanceof L2PcInstance) {
								env.target.sendPacket(new EtcStatusUpdate(
										(L2PcInstance) env.target));
								SystemMessage sm = new SystemMessage(
										SystemMessageId.FORCE_INCREASED_TO_S1);
								sm.addNumber(effectcharge);
								env.target.sendPacket(sm);
							}
						}
					} else
						effects.add(e);
				} else
					effects.add(e);
		}
		if (effects.size() == 0)
			return _emptyEffectSet;
		return effects.toArray(new L2Effect[effects.size()]);
	}

	public final void attach(FuncTemplate f) {
		if (_funcTemplates == null)
			_funcTemplates = new FuncTemplate[] { f };
		else {
			int len = _funcTemplates.length;
			FuncTemplate[] tmp = new FuncTemplate[len + 1];
			System.arraycopy(_funcTemplates, 0, tmp, 0, len);
			tmp[len] = f;
			_funcTemplates = tmp;
		}
	}

	public final void attach(EffectTemplate effect) {
		if (_effectTemplates == null)
			_effectTemplates = new EffectTemplate[] { effect };
		else {
			int len = _effectTemplates.length;
			EffectTemplate[] tmp = new EffectTemplate[len + 1];
			System.arraycopy(_effectTemplates, 0, tmp, 0, len);
			tmp[len] = effect;
			_effectTemplates = tmp;
		}
	}

	public final void attachSelf(EffectTemplate effect) {
		if (_effectTemplatesSelf == null)
			_effectTemplatesSelf = new EffectTemplate[] { effect };
		else {
			int len = _effectTemplatesSelf.length;
			EffectTemplate[] tmp = new EffectTemplate[len + 1];
			System.arraycopy(_effectTemplatesSelf, 0, tmp, 0, len);
			tmp[len] = effect;
			_effectTemplatesSelf = tmp;
		}
	}

	public final void attach(Condition c, boolean itemOrWeapon) {
		if (itemOrWeapon)
			_itemPreCondition = c;
		else
			_preCondition = c;
	}

	@Override
	public String toString() {
		return "" + _name + "[id=" + _id + ",lvl=" + _level + "]";
	}

	public int getAugmentationType() {
		return augmentationType;
	}

	public boolean isAugmentationSkill() {
		return getAugmentationType() > -1 ? true : false;
	}
}